package be.jaisy.ui.gwt.user.server.rcp;

import java.lang.reflect.Method;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import be.jaisy.osgi.IBundleContextAware;
import be.jaisy.ui.gwt.Activator;
import be.jaisy.ui.gwt.api.OSGIServiceAccessException;

import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;

public class OsgiServletService extends RemoteServiceServlet implements IBundleContextAware {
	private static final long serialVersionUID = 1299502008006943122L;

	private BundleContext bundleContext;

	public OsgiServletService() {
		super();
	}

	public void setBundleContext(BundleContext context) {
		bundleContext = context;
	}

	public String processCall(String payload) throws SerializationException {
		Method method = null;
		try {		
			ServerSerializationStreamReader streamReader = OsgiRPC.prepareToRead(payload);			
			String interfaceClassName = OsgiRPC.decodeInterfaceClassName(streamReader);						
			ServiceReference serviceReference = findServiceReference(interfaceClassName);
			RPCRequest rpcRequest = OsgiRPC.decodeRequest(streamReader, interfaceClassName, serviceReference.getBundle().getBundleContext(), payload);
			method = rpcRequest.getMethod();
			try {
				Object service = bundleContext.getService(serviceReference);
				if (service == null) {
					throw new OSGIServiceAccessException("osgi service " + interfaceClassName + " not accessible from service reference " + serviceReference);
				}
				Activator.logDebug(this.getClass(), "processCall("+method.getDeclaringClass().getName()+","+method.getName()+"())...");
				return OsgiRPC.invokeAndEncodeResponse(service, method, rpcRequest.getParameters());
			} finally {
				bundleContext.ungetService(serviceReference);
			}			
		} catch (Throwable ex) {
			if(method!=null && method.getDeclaringClass()!=null)
				Activator.logWarning(this.getClass(), "processCall("+method.getDeclaringClass().getName()+","+method.getName()+"(...)) failed", ex);
			else
				Activator.logWarning(this.getClass(), "processCall(?) failed", ex);
			return OsgiRPC.encodeResponseForFailure(method, ex);
		}
	}
	
	private ServiceReference findServiceReference(String interfaceClassName){
		ServiceReference serviceReference = bundleContext.getServiceReference(interfaceClassName);
		if (serviceReference == null) {
			throw new OSGIServiceAccessException("no services are registered which implement the named interface class [" + interfaceClassName+"]");
		}		
		return serviceReference;
	}	
}
